import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, Lasso
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.preprocessing import PolynomialFeatures
from sklearn.ensemble import RandomForestRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.neighbors import KNeighborsRegressor

# 加载数据集
file_path = r"D:\PYCHARM文件\zwz050418\main\house.xlsx"
df = pd.read_excel(file_path)

# 删除网址和挂牌时间列
df.drop(columns=['网址', '挂牌时间'], inplace=True)

# 查看数据总体情况
print(f"样本量共有 {df.shape[0]} 个")

# 将 "暂无" 和 "NaN" 替换为 np.nan
df.replace(['暂无', 'NaN', '无', 'nan'], np.nan, inplace=True)

# 计算每列的缺失值数量和缺失值比例，并按降序排序
missing_values = df.isnull().sum().sort_values(ascending=False)
total_samples = df.shape[0]
missing_percentage = (missing_values / total_samples) * 100

# 创建包含缺失值数量和缺失值比例的 DataFrame
missing_data = pd.DataFrame({'缺失值数量': missing_values, '缺失值比例': missing_percentage})

# 打印缺失值情况
print("缺失值情况（按缺失值比例排序）:")
print(missing_data)

# 删除车位数量
df.drop(columns=['车位数量'], inplace=True)

# 将车库数量列的所有缺失值改为 0
df.fillna({'车库数量': 0}, inplace=True)

# 将有无电梯列的所有缺失值改为 0
df.fillna({'有无电梯': 0}, inplace=True)

# 将部分缺失值替换为零
df.fillna({'产权性质': 0}, inplace=True)
df.fillna({'住宅类别': 0}, inplace=True)
df.fillna({'装修程度': 0}, inplace=True)
df.fillna({'建筑结构': 0}, inplace=True)
df.fillna({'建筑形式': 0}, inplace=True)

# 删除完全缺失的列
df.dropna(axis=1, how='all', inplace=True)

# 打印处理缺失值后的数据量
print(f"初步处理的数据样本量: {df.shape[0]}")

# 查看数据类型
data_types = df.dtypes
print("数据类型:")
print(data_types)

# 处理建筑年代
df['建筑年代'] = df['建筑年代'].astype(str)
df['建筑年代'] = df['建筑年代'].str.extract(r'(\d{4})')[0]
df['建筑年代'] = df['建筑年代'].fillna(0).astype('int64')
df['房龄'] = df['建筑年代'].map(lambda x: 2024 - x if x != 0 else np.nan)
df.drop(columns=['建筑年代'], inplace=True)

# 分解户型
def apart_room(x):
    try:
        room = x.split('室')[0]
        return int(room)
    except:
        return None

def apart_hall(x):
    try:
        hall = x.split('厅')[0].split('室')[1]
        return int(hall)
    except:
        return None

def apart_wc(x):
    try:
        wc = x.split('卫')[0].split('厅')[1]
        return int(wc)
    except:
        return None

df['室'] = df['户型'].map(apart_room)
df['厅'] = df['户型'].map(apart_hall)
df['卫'] = df['户型'].map(apart_wc)

# 删除户型列，因为它仍然是字符串类型
df.drop(columns=['户型', '楼层', '单价'], inplace=True)

print(f"处理总价后的数据量: {df.shape[0]}")

# 删除无法解析户型信息的行
df = df.dropna(subset=['室', '厅', '卫'])
print(f"处理户型信息后的数据量: {df.shape[0]}")

# 编码 - 有序多分类
map1 = {'南': 2, '南北': 2, '北': 3, '西南': 6, '东西': 2, '东': 4, '东北': 6, '东南': 3, '西': 3, '西北': 6, '东南北': 3, '西南北': 5, '东西北': 4, '东西南': 3, '东西南北': 1}
df['朝向'] = df['朝向'].map(map1)

map2 = {'毛坯': 1, '简装修': 2, '精装修': 4, '中装修': 3, '豪华装修': 5}
df['装修程度'] = df['装修程度'].map(map2)

map3 = {'有 ': 2, '无 ': 1}
df['有无电梯'] = df['有无电梯'].map(map3)

map4 = {'商品房': 4, '个人产权': 1, '单位产权': 5, '经济适用房': 2, '成本价房改房': 3, '限价房': 6, '使用权': 2}
df['产权性质'] = df['产权性质'].map(map4)

map5 = {'普通住宅': 1, '四合院': 3, '公寓': 3, '商业办公类': 5, '酒店式公寓': 5}
df['住宅类别'] = df['住宅类别'].map(map5)

map6 = {'平层': 4, '平层 ': 4, '开间': 2, '跃层': 5, '错层': 1, '复式': 3}
df['建筑结构'] = df['建筑结构'].map(map6)

map7 = {'板楼': 4, '钢混': 5, '塔板结合': 3, '平房': 6, '砖混': 1, '塔楼': 7, '砖楼': 2}
df['建筑形式'] = df['建筑形式'].map(map7)

# 编码地区
map_district = {' 朝阳 ': 1, ' 东城 ': 2, ' 西城 ': 3, ' 顺义 ': 4, ' 昌平 ': 5, ' 密云 ': 6, ' 房山 ': 7, ' 通州 ': 8, ' 燕郊 ': 9, ' 海淀 ': 10,
               ' 门头沟 ': 11, ' 丰台 ': 12, ' 大兴 ': 13, ' 怀柔 ': 14, ' 石景山 ': 15, ' 北京周边 ': 16}
df['地区'] = df['地区'].map(map_district)

# 统计分析：计算各户型的数量和平均总价
house_type_grouped_data = df.groupby(['室', '厅', '卫']).agg(
    平均总价=('总价', 'mean'),
    数量=('总价', 'size')
).reset_index()

# 显示不同户型的统计结果
print("\n不同户型的统计结果:")
print(house_type_grouped_data)
print('--------------------------------')


# 准备训练和测试数据
X = df.drop(columns=['总价'])
y = df['总价']
# 确保所有特征都是数值类型，并处理 NaN、无穷大或过大的数值
for col in X.columns:
    # 检查列中是否有 NaN 值
    if X[col].isnull().any():
        # 用列的中位数替换 NaN 值
        median_value = X[col].median()
        X[col] = X[col].fillna(median_value)

    # 检查并替换无穷大值
    X[col] = X[col].replace([np.inf, -np.inf], np.nan)
    X[col] = X[col].fillna(0)  # 用0或其他适当的值替换无穷大值

# 分割数据集
x_train, x_test, y_train, y_test = train_test_split(X, y, random_state=33)

# 多项式特征转换
poly = PolynomialFeatures(degree=2)
x_train_poly = poly.fit_transform(x_train)
x_test_poly = poly.transform(x_test)

# 套索回归
print('--------------------------------')
la = Lasso(alpha=0.1, max_iter=100000)
la.fit(x_train_poly, y_train)
print(f'套索回归训练集得分：{round(la.score(x_train_poly, y_train), 2)}')
print(f'测试集得分：{round(la.score(x_test_poly, y_test), 2)}')

# 随机森林
print('--------------------------------')
rf = RandomForestRegressor()
rf.fit(x_train_poly, y_train)
print(f'随机森林训练集得分：{round(rf.score(x_train_poly, y_train), 2)}')
print(f'测试集得分：{round(rf.score(x_test_poly, y_test), 2)}')

# 决策树
print('--------------------------------')
dt = DecisionTreeRegressor(max_depth=6)
dt.fit(x_train_poly, y_train)
print(f'决策树训练集得分：{round(dt.score(x_train_poly, y_train), 2)}')
print(f'测试集得分：{round(dt.score(x_test_poly, y_test), 2)}')

# k近邻
print('--------------------------------')
kn = KNeighborsRegressor(n_neighbors=20)
kn.fit(x_train_poly, y_train)
print(f'k近邻训练集得分：{round(kn.score(x_train_poly, y_train), 2)}')
print(f'测试集得分：{round(kn.score(x_test_poly, y_test), 2)}')

# 应用多项式特征转换到新数据
apply = np.array([95, 10, 4, 0, 5, 4, 4, 5, 6, 1, 3, 1, 1, 10]).reshape(1, -1)
poly_apply = poly.transform(apply)
print('------------总价预测结果-------------')
print(f'线性回归：{round(la.predict(poly_apply)[0], 2)}万元')
print(f'随机森林回归：{round(rf.predict(poly_apply)[0], 2)}万元')
print(f'决策树回归：{round(dt.predict(poly_apply)[0], 2)}万元')
print(f'K近邻回归：{round(kn.predict(poly_apply)[0], 2)}万元')
print('------------综合预测结果-------------')
print(round(((la.predict(poly_apply) + rf.predict(poly_apply) + dt.predict(poly_apply) + kn.predict(poly_apply)) / 4.0)[0], 2), '万元')
